package cn.itcast.protocol;

import cn.itcast.message.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;

@Slf4j
@ChannelHandler.Sharable
/**
 * 必须和 LengthFieldBasedFrameDecoder 一起使用，确保接到的 ByteBuf 消息是完整的 就是刚刚那个处理半包闭包问题的处理器
 * 逻辑跟MessageCodec的没区别 只是为了可以使用@ChannelHandler.Sharable注解而已我们大概改造一下
 */
public class MessageCodecSharable extends MessageToMessageCodec<ByteBuf, Message> {
    @Override
    public void encode(ChannelHandlerContext ctx, Message msg, List<Object> outList) throws Exception {
        ByteBuf out = ctx.alloc().buffer();
        // 1. 4 字节的魔数
        out.writeBytes(new byte[]{1, 2, 3, 4}); //writeBytes从ByteBuf的可写的位置开始写入一个字节数组 内容是1234
        // 2. 1 字节的版本,
        out.writeByte(1);//writeByte从ByteBuf的可写的位置开始开始写入一个字节 内容是1
        // 3. 1 字节的序列化方式 jdk序列化/反序列化 0 , json格式 1
        out.writeByte(0);
        // 4. 1 字节的指令类型
        out.writeByte(msg.getMessageType());
        // 5. 4 个字节的请求序号
        out.writeInt(msg.getSequenceId()); ////writeInt从ByteBuf的可写的位置开始开始写入一个Int (4个字节)
        // 无意义，对齐填充 因为除了内容以外的这种协议规范目前总共有15个字节 但是在协议的规范中一般得是2的整数倍 所以我们这里加一个让其规范
        out.writeByte(0xff);
        // 6. 获取内容的字节数组
        //一个对象怎么变成二进制数据? 只要一个对象实现了序列化接口他在网络传输肯定就直接转换为二进制了
        //这里我们利用两个类，把一个对象直接写出成二进制数据byte数组 前提是这个类得实现序列化接口
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(msg);
        byte[] bytes = bos.toByteArray();
        // 7. 长度
        out.writeInt(bytes.length);
        // 8. 写入内容
        out.writeBytes(bytes);

        outList.add(out); //List<Object> outList 出战处理器会从这里面去数据所以我们处理完就把数据添加进去
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        //读取出四个字节的魔数
        int magicNum = in.readInt();
        //拿到一个字节的版本
        byte version = in.readByte();
        //拿到序列化方式 1个字节
        byte serializerType = in.readByte();
        //1 字节的指令类型
        byte messageType = in.readByte();
        //消息序号
        int sequenceId = in.readInt();
        //填充的字节 读出来不接收
        in.readByte();
        //内容长度
        int length = in.readInt();
        //创建一个接收内容的字节数组(可以放二进制数据)
        byte[] bytes = new byte[length];
        in.readBytes(bytes, 0, length); //把数据写到bytes中从0开始写length那么多个
        if(serializerType == 0){ //如果是jdk的序列化/反序列化
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
            //把二进制数据反序列化为对象
            Message message = (Message) ois.readObject();
            log.debug("{}, {}, {}, {}, {}, {}", magicNum, version, serializerType, messageType, sequenceId, length);
            log.debug("{}", message);
            out.add(message);
        }
    }

}
